/**
 * Copyright (c) 2011 eelpo.com
 * All rights reserved.
 */
package com.eelpo.common.security;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.security.Key;
import java.security.SecureRandom;

import javax.crypto.Cipher;
import javax.crypto.CipherInputStream;
import javax.crypto.CipherOutputStream;
import javax.crypto.spec.IvParameterSpec;

import com.eelpo.common.encoder.BASE64DecoderStream;
import com.eelpo.common.encoder.BASE64EncoderStream;
import com.eelpo.common.security.constant.Constants;
import com.eelpo.common.util.BlankUtils;
import com.eelpo.common.util.StringUtils;

/**
 * 功能描述：信息加密，私鈅加密器，保证消息机密性(对称算法)，支持AES,（TripleDES）DESede，Blowfish算法，推荐使用AES算法
 * <p>
 * 版权所有：广州市艾尔博科技有限公司
 * <p>
 * 未经本公司许可，不得以任何方式复制或使用本程序任何部分
 * 
 * @author <a href="mailto:Brian@eelpo.com">汪洪州</a>
 * 
 * @since 1.0
 */
public class SymmetryAlgorithmEncryptor {

	// 密钥
	private Key key;

	// 算法
	private String algorithm;

	// iv长度
	private int ivlength;

	/**
	 * constructor
	 * 
	 */
	public SymmetryAlgorithmEncryptor() {
		this(KeyBuilder.getInstance().getSymmetryKey(Constants.AES_ALGORITHM, Constants.AES_KEYSIZE), Constants.AES_ALGORITHM);
	}

	/**
	 * constructor
	 * 
	 * @param key
	 *            ：密钥
	 * @param algorithm
	 *            ：对称算法
	 */
	public SymmetryAlgorithmEncryptor(Key key, String algorithm) {

		this.key = key;
		this.algorithm = algorithm;

		ivlength = Constants.AES_ALGORITHM.equals(algorithm) ? Constants.AES_IVLENGTH : ivlength;
		ivlength = Constants.TRIPLEDES_ALGORITHM.equals(algorithm) ? Constants.TRIPLEDES_IVLENGTH : ivlength;
		ivlength = Constants.BLOWFISH_ALGORITHM.equals(algorithm) ? Constants.BLOWFISH_IVLENGTH : ivlength;

	}

	public String getAlgorithm() {
		return algorithm;
	}

	public void setAlgorithm(String algorithm) {
		this.algorithm = algorithm;
	}

	public Key getKey() {
		return key;
	}

	public void setKey(Key key) {
		this.key = key;
	}

	/**
	 * <p>
	 * 方法用途和描述：对字符串进行加密
	 * </p>
	 * 
	 * @param plainText
	 *            ：要加密的字符串
	 * @return 返回字符串密文
	 */
	public String encrypt(String plainText) {

		if (BlankUtils.isBlank(plainText)) {
			return StringUtils.EMPTY;
		}

		return BASE64EncoderStream.encode(encrypt(plainText.getBytes()));

	}

	/**
	 * <p>
	 * 方法用途和描述：对字符数组进行加密
	 * </p>
	 * 
	 * @param plainBytes
	 *            ：要加密的字符数组
	 * @return 返回字符数组密文
	 */
	public byte[] encrypt(byte[] plainBytes) {

		if (BlankUtils.isBlank(plainBytes)) {
			return new byte[] {};
		}

		return execute(Cipher.ENCRYPT_MODE, plainBytes);

	}

	/**
	 * <p>
	 * 方法用途和描述：对文件进行加密
	 * </p>
	 * 
	 * @param inputPath
	 *            ：要加密的文件路径
	 * @param outputPath
	 *            ：加密后的密文输出文件路径
	 */
	public void encrypt(String inputPath, String outputPath) {

		try {

			InputStream inputStream = new FileInputStream(inputPath);
			OutputStream outputStream = new FileOutputStream(outputPath);

			SecureRandom random = new SecureRandom();
			byte[] iv = new byte[ivlength];
			random.nextBytes(iv);

			Cipher cipher = getCipher(Cipher.ENCRYPT_MODE, iv);

			outputStream.write(iv);
			CipherOutputStream cipherStream = new CipherOutputStream(outputStream, cipher);

			for (int i = inputStream.read(); i != -1; i = inputStream.read())
				cipherStream.write(i);

			inputStream.close();
			cipherStream.close();
			outputStream.close();

		} catch (Exception e) {
			e.printStackTrace();
		}

	}

	/**
	 * <p>
	 * 方法用途和描述：对字符串进行解密
	 * </p>
	 * 
	 * @param cipherText
	 *            ：要解密的字符串密文
	 * @return 返回明文信息
	 */
	public String decrypt(String cipherText) {

		if (BlankUtils.isBlank(cipherText)) {
			return StringUtils.EMPTY;
		}

		return new String(decrypt(BASE64DecoderStream.decode(cipherText)));
	}

	/**
	 * <p>
	 * 方法用途和描述：对字符数组进行解密
	 * </p>
	 * 
	 * @param cipherBytes
	 *            ：要解密的字符数组
	 * @return 返回字符数组明文
	 */
	public byte[] decrypt(byte[] cipherBytes) {

		if (BlankUtils.isBlank(cipherBytes)) {
			return new byte[] {};
		}

		return execute(Cipher.DECRYPT_MODE, cipherBytes);

	}

	/**
	 * <p>
	 * 方法用途和描述：对文件进行解密
	 * </p>
	 * 
	 * @param inputPath
	 *            ：要解密的密文文件路径
	 * @param outputPath
	 *            ：解密后的明文输出文件路径
	 */
	public void decrypt(String inputPath, String outputPath) {

		try {

			InputStream inputStream = new FileInputStream(inputPath);
			OutputStream outputStream = new FileOutputStream(outputPath);

			byte[] iv = new byte[ivlength];
			inputStream.read(iv);

			Cipher cipher = getCipher(Cipher.DECRYPT_MODE, iv);

			CipherInputStream cipherStream = new CipherInputStream(inputStream, cipher);

			for (int i = cipherStream.read(); i != -1; i = cipherStream.read())
				outputStream.write(i);

			inputStream.close();
			cipherStream.close();
			outputStream.close();

		} catch (Exception e) {
			e.printStackTrace();
		}

	}

	/*
	 * <p> 方法用途和描述：执行加密解密 </p> @param mode：加密解密器的模式
	 * 
	 * @param data：加密解密数据
	 * 
	 * @return 返回加密解密后的信息
	 */
	private byte[] execute(int mode, byte[] data) {

		try {

			Cipher cipher = Cipher.getInstance(algorithm.concat(Constants.ECB_PKCS5PADDING));

			cipher.init(mode, key);

			return cipher.doFinal(data);

		} catch (Exception e) {
			e.printStackTrace();
			return null;
		}

	}

	/*
	 * <p> 方法用途和描述：获取加密解密器 </p>
	 * 
	 * @param mode：要获取加密解密器的模式
	 * 
	 * @param iv：算法的IV参数
	 * 
	 * @return 返回加密解密器
	 */
	private Cipher getCipher(int mode, byte[] iv) {

		IvParameterSpec spec = new IvParameterSpec(iv);

		try {

			Cipher cipher = Cipher.getInstance(algorithm.concat(Constants.CBC_PKCS5PADDING));
			cipher.init(mode, key, spec);

			return cipher;
		} catch (Exception e) {
			e.printStackTrace();
			return null;
		}

	}
}
